﻿using System;
using System.ComponentModel;
using System.IO;
using System.Linq.Expressions;
using nRoute.Components;
using Orktane.Components;

namespace Orktane.Controls
{
    public abstract class ConsoleBase
        : IConsole, INotifyPropertyChanged
    {

#region Declarations

        private const string DEFAULT_TITLE = "Console";
        private const int DEFAULT_BUFFER_LENGTH = Int16.MaxValue;

        //private TextWriter _error;
        private TextReader _in;
        private TextWriter _out;
        private ReadInputMode _inputMode;
        private ConsoleColor _backgroundColor = ConsoleColor.DarkBlue;
        private ConsoleColor _foregroundColor = ConsoleColor.White;
        private string _title = DEFAULT_TITLE;
        private string _disabledInputText;
        private Action<ConsoleKeyInfo> _readKeyAction;
        private Action<string> _readLineAction;
        private string _prompt;
        private bool _disableInput;
        private int _outBufferLength = DEFAULT_BUFFER_LENGTH;

#endregion

        //public Console(TextReader inReader, TextWriter outWriter)  
        //    : this(inReader, outWriter, null) { }

        //public ConsoleBase(TextReader inReader, TextWriter outWriter) //, TextWriter errorWriter)
        //{
        //    if (inReader == null) throw new ArgumentNullException("inReader");
        //    if (outWriter == null) throw new ArgumentNullException("outReader");
        //    //if (errorWriter == null) errorWriter = outWriter;

        //    _in = inReader;
        //    _out = outWriter;
        //    //_error = errorWriter;
        //}

#region Properties

        protected Action<ConsoleKeyInfo> ReadKeyAction
        {
            get { return _readKeyAction; }
        }

        protected Action<string> ReadLineAction
        {
            get { return _readLineAction; }
        }

        protected int OutBufferLength
        {
            get { return _outBufferLength; }
        }

        public ReadInputMode InputMode
        {
            get { return _inputMode; }
            set
            {
                if (_inputMode != value)
                {
                    _inputMode = value;
                    OnInputReadModeChanged();
                    PropertyChanged.Notify(() => InputMode);
                }
            }
        }

#endregion

#region IConsole Members

        // note we need to use, this as we don't/can't have blocking calls in silverlight
        //public event EventHandler OutputAvaliable;
        //public event EventHandler InputAvaliable;
        public event EventHandler CancelKeyPress;

        public bool DisableInput
        {
            get { return _disableInput; }
            set
            {
                if (_disableInput != value)
                {
                    _disableInput = value;
                    OnInputDisabledChanged();
                    PropertyChanged.Notify(() => DisableInput);
                }
            }
        }

        public string DisabledInputText
        {
            get
            {
                return _disabledInputText;
            }
            set
            {
                if (!string.Equals(_disabledInputText, value, StringComparison.InvariantCulture))
                {
                    _disabledInputText = value;
                    PropertyChanged.Notify(() => DisabledInputText);
                }
            }
        }

        public string Prompt
        {
            get
            {
                return _prompt;
            }
            set
            {
                if (!string.Equals(_prompt, value, StringComparison.InvariantCulture))
                {
                    OnPromptChanged(_prompt, value);
                    _prompt = value;
                    PropertyChanged.Notify(() => Prompt);
                }
            }
        }

        public ConsoleColor BackgroundColor
        {
            get
            {
                return _backgroundColor;
            }
            set
            {
                if (_backgroundColor != value)
                {
                    _backgroundColor = value;
                    PropertyChanged.Notify(() => BackgroundColor);
                }
            }
        }

        public ConsoleColor ForegroundColor
        {
            get
            {
                return _foregroundColor;
            }
            set
            {
                if (_foregroundColor != value)
                {
                    _foregroundColor = value;
                    PropertyChanged.Notify(() => ForegroundColor);
                }
            }
        }

        public string Title
        {
            get
            {
                return _title;
            }
            set
            {
                if (!string.Equals(_title, value, StringComparison.InvariantCulture))
                {
                    _title = value;
                    PropertyChanged.Notify(() => Title);
                }
            }
        }

        //public ConsoleKeyInfo ReadKey()
        //{
        //    return _lastReadKey;
        //}

        public void ReadKey(Action<ConsoleKeyInfo> readKeyAction)
        {
            if (readKeyAction == null) throw new ArgumentNullException("readKeyAction");
            _readKeyAction = readKeyAction;
            InputMode = ReadInputMode.ReadKey;
        }

        //public string ReadLine()
        //{
        //    return In.ReadLine();
        //}

        public void ReadLine(Action<string> readLineAction)
        {
            if (readLineAction == null) throw new ArgumentNullException("readLineAction");
            _readLineAction = readLineAction;
            InputMode  = ReadInputMode.ReadLine;
        }

        public void ClearOutput()
        {
            OnClearOutput();
        }

        public void ClearInput()
        {
            OnClearInput();
        }

        public void Beep()
        {
            OnBeep();
        }

        public void SetOutBuffer(int length)
        {
            if (length < 1) throw new ArgumentOutOfRangeException("length");
            if (length != _outBufferLength)
            {
                _outBufferLength = length;
                OnBufferLengthChanged();
                PropertyChanged.Notify(() => OutBufferLength);
            }
        }

        public void Write(char[] buffer)
        {
            Out.Write(buffer);
            OnWrite();
        }

        public void Write(char value)
        {
            Out.Write(value);
            OnWrite();
        }

        public void Write(int value)
        {
            Out.Write(value);
            OnWrite();
        }

        public void Write(object value)
        {
            Out.Write(value);
            OnWrite();
        }

        public void Write(string value)
        {
            Out.Write(value);
            OnWrite();
        }

        public void Write(string format, params object[] arg)
        {
            Out.Write(format, arg);
            OnWrite();
        }

        public void Write(string format, object arg0)
        {
            Out.Write(format, arg0);
            OnWrite();
        }

        public void Write(char[] buffer, int index, int count)
        {
            Out.Write(buffer, index, count);
            OnWrite();
        }

        public void Write(string format, object arg0, object arg1)
        {
            Out.Write(format, arg0, arg1);
            OnWrite();
        }

        public void Write(string format, object arg0, object arg1, object arg2)
        {
            Out.Write(format, arg0, arg1, arg2);
            OnWrite();
        }

        public void WriteLine()
        {
            Out.WriteLine();
            OnWrite();
        }

        public void WriteLine(char value)
        {
            Out.WriteLine(value);
            OnWrite();
        }

        public void WriteLine(int value)
        {
            Out.WriteLine(value);
            OnWrite();
        }

        public void WriteLine(char[] buffer)
        {
            Out.WriteLine(buffer);
            OnWrite();
        }

        public void WriteLine(object value)
        {
            Out.WriteLine(value);
            OnWrite();
        }

        public void WriteLine(string value)
        {
            Out.WriteLine(value);
            OnWrite();
        }

        public void WriteLine(string format, params object[] arg)
        {
            Out.WriteLine(format, arg);
            OnWrite();
        }

        public void WriteLine(string format, object arg0)
        {
            Out.WriteLine(format, arg0);
            OnWrite();
        }

        public void WriteLine(string format, object arg0, object arg1)
        {
            Out.WriteLine(format, arg0, arg1);
            OnWrite();
        }

        public void WriteLine(string format, object arg0, object arg1, object arg2)
        {
            Out.WriteLine(format, arg0, arg1, arg2);
            OnWrite();
        }

        //protected TextWriter Error
        //{
        //    get { return _error; }
        //}

        protected TextReader In
        {
            get  { return _in; }
        }

        protected TextWriter Out
        {
            get { return _out; }
        }

#endregion

#region Protected Memebers

        //protected void SetError(TextWriter newError)
        //{
        //    if (newError == null) throw new ArgumentNullException("newError");
        //    _error = newError;
        //}

        protected void SetIn(TextReader newIn)
        {
            if (newIn == null) throw new ArgumentNullException("newIn");
            _in = newIn;
        }

        protected void SetOut(TextWriter newOut)
        {
            if (newOut == null) throw new ArgumentNullException("newOut");
            _out = newOut;
        }
            
        protected virtual void OnInputDisabledChanged() { }

        protected virtual void OnBufferLengthChanged() { }

        protected abstract void OnInputReadModeChanged();

        protected abstract void OnPromptChanged(string oldPrompt, string newPrompt);

        protected abstract void OnWrite();

        protected abstract void OnBeep();

        protected abstract void OnClearOutput();

        protected abstract void OnClearInput();

        protected virtual void RaiseCancelKeyPress()
        {
            if (CancelKeyPress != null) CancelKeyPress(this, EventArgs.Empty);
        }

        //protected virtual void RaiseOutputAvaliable()
        //{
        //    if (OutputAvaliable != null) OutputAvaliable(this, EventArgs.Empty);
        //}

        //protected virtual void RaiseInputAvaliable()
        //{
        //    if (InputAvaliable != null) InputAvaliable(this, EventArgs.Empty);
        //}

#endregion

#region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyPropertyChanged<T>(Expression<Func<T>> propertySelector)
        {
            if (propertySelector == null) throw new ArgumentNullException("propertySelector");
            PropertyChanged.Notify<T>(propertySelector);
        }

#endregion

#region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_in != null)
                {
                    _in.Dispose();
                    _in = null;
                }
                if (_out != null)
                {
                    _out.Dispose();
                    _out = null;
                }
                if (_readLineAction != null) _readLineAction = null;
            }
        }

#endregion

    }
}
